package brave;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;

import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.WorldCreator;

public class WallGame
{
	private World bukkitWorld;
	
	// TODO: make sure all observers get the timer messages and in game players messeges
	private ArrayList<WallPlayer> observers;
	private WallTeam[] teams;

	// TODO: get the task id and cancel it
	// TODO: just fix the timer stuff
	private RunCountdownTimer runCountdownTimer;
	private boolean worldReadyForPlayers;
	
	private int destroyWallTaskId;
	private boolean gameStarted;
	private int gameId;
	private int gameTimeInMin;
	
	// timer tasks
	private class RunCountdownTimer implements Runnable
	{
		private int time;
		
		public RunCountdownTimer(int timeInSeconds)
		{
			this.time = timeInSeconds;
		}

		
		@Override
		public void run()
		{
			// every 5 minutes
			if (this.time >= (5 * 60) && this.time % (5 * 60) == 0)
			{
				dispTime(this.time);				
			}
			else if (this.time < (5 * 60) && this.time >= (1 * 60) && this.time % (1 * 60) == 0)
			{
				dispTime(this.time);
			}
			else if (this.time < 30 && this.time >= 0)
			{
				dispTime(this.time);
			}
			else if (this.time < 0)
			{
				destroyWall();
			}
			this.time -= 1;
		}		
	}
	
	public WallGame(World world, int timeLeft, int id)
	{
		this.bukkitWorld = world;
		this.bukkitWorld.setMonsterSpawnLimit(0);
		this.gameTimeInMin = timeLeft;
		this.runCountdownTimer = new RunCountdownTimer(this.gameTimeInMin * 60);
		this.worldReadyForPlayers = true;
		this.gameStarted = false;
		this.gameId = id;

		this.observers = new ArrayList<WallPlayer>();
		this.teams = new WallTeam[4];
		for (int i = 0; i < 4; i++)
		{
			this.teams[i] = new WallTeam();
		}		
	}

	public void addObserver(WallPlayer player)
	{
		observers.add(player);		
	}
	
	/**
	 * 
	 * @param player
	 * @param team
	 * @return slotNum player was added to in team
	 */
	public Integer addPlayer(WallPlayer player, Integer team)
	{
		if (this.gameStarted)
		{
			return null;
		}
		
		
		// if the world is not ready send them away with a message
		if (!this.worldReadyForPlayers)
		{
			return null;
		}

		Integer teamSlot = teams[team].addPlayer(player);		
		return teamSlot;
	}
	
	public boolean isGameReadyToAddPlayers()
	{
		return this.worldReadyForPlayers;
	}
	
	public boolean isGameReadyToStart()
	{
		if (this.getNumPlayers() == 12 || this.areTeamsEven())
		{
			return true;
		}
		
		return false;
	}

	private boolean areTeamsEven()
	{
		if (getNumPlayers() <= 0)
		{
			return false;
		}
		
		int sameNum = teams[0].getNumPlayers();
		for (int i = 1; i < 4; i++)
		{
			if (sameNum != teams[i].getNumPlayers())
			{
				return false;
			}
		}
		
		return true;
	}

	private int getNumPlayers()
	{
		int numPlayers = 0;
		for (int i = 0; i < 4; i++)
		{
			numPlayers += this.teams[i].getNumPlayers();
		}
		return numPlayers;
	}

	public void dispTime(int time)
	{
		for (int i = 0; i < 4; i++)
		{
			this.teams[i].dispTime(time);
		}
	}
	
	private void destroyWall()
	{
		destroyWallTaskId = Bukkit.getScheduler().scheduleAsyncRepeatingTask(
				Brave.instance,
				new Runnable() {
					int y = 141;
					
					@Override
					public void run()
					{
						if (y >= 116)
						{
							for (int z = -855, x = 285; z <= -731 && x <= 409; z++, x++)
							{
								bukkitWorld.getBlockAt(347, y, z).setType(Material.AIR);
								bukkitWorld.getBlockAt(x, y, -793).setType(Material.AIR);
							}
						}
						
						y--;
					
						if (y < 116)
						{
							System.out.println("should destroy task");
							// TODO we need to make this only its own task
							Bukkit.getScheduler().cancelTasks(Brave.instance);
						}
					}
				}, 0,  1 * 20);
	}
	
	private void reGenWall()
	{
		destroyWallTaskId = Bukkit.getScheduler().scheduleAsyncRepeatingTask(
				Brave.instance,
				new Runnable() {
					int y = 141;
					
					@Override
					public void run()
					{
						for (int z = -855, x = 285; z <= -731 && x <= 409; z++, x++)
						{
							bukkitWorld.getBlockAt(347, y, z).setType(Material.BEDROCK);
							bukkitWorld.getBlockAt(x, y, -793).setType(Material.BEDROCK);
						}
						
						y--;
					
						if (y <= 115)
						{
							Bukkit.getScheduler().cancelTask(destroyWallTaskId);
						}
					}
				}, 0,  1 * 10);
	}
	
	private void letPlayersOut()
	{
		for (int i = 0; i < 4; i++)
		{
			for (int j = 0; j < 3; j++)
			{
				Location location = Brave.getTeamLocation(gameId, i, j);
				World world = location.getWorld();
				int y = (int) location.getY() + 1;
				int xstart = (int) location.getX();
				int zstart = (int) location.getZ();
				for (int x = xstart - 2; x <= xstart + 2; x++)
				{
					for (int z = zstart - 2; z <= zstart + 2; z++)
					{
						world.getBlockAt(x, y, z).setType(Material.AIR);
						world.getBlockAt(x, y + 1, z).setType(Material.AIR);
					}
				}
			}
		}	
	}
	
	public void start()
	{
		this.gameStarted = true;
		letPlayersOut();
		// TODO: fix leak by getting the task id and removing it when done
		Bukkit.getScheduler().scheduleAsyncRepeatingTask(
				Brave.instance, this.runCountdownTimer, 0,  1 * 20);
	}

	public void stop()
	{
		// stop people from coming to the world
		this.worldReadyForPlayers = false;

		// tp all players to the spawn
		for (int i = 0; i < 4; i++)
		{
			for (int j = 0; j < teams[i].getNumPlayers(); j++)
			{
				WallPlayer wallPlayer = teams[i].getPlayer(j);
				wallPlayer.setState(WallPlayerState.DEFAULT);
				tpToOrigin(wallPlayer);
			}
		}
		
		// tp all observers to spawn
		for (WallPlayer wallPlayer : observers)
		{
			wallPlayer.setState(WallPlayerState.DEFAULT);
			tpToOrigin(wallPlayer);
		}
		
		// reset the world
		reset();
	}

	private void tpToOrigin(WallPlayer wallPlayer)
	{
		wallPlayer.getBukkitPlayer().teleport(Brave.origin_spawn_location);	
	}

	public void reset()
	{
		Bukkit.getScheduler().cancelTasks(Brave.instance);

		Bukkit.getScheduler().scheduleSyncDelayedTask(
				Brave.instance,
				new Runnable()
				{
					@Override
					public void run()
					{
						Bukkit.getServer().unloadWorld(bukkitWorld, true);
						File walls0 = new File("walls0");
						File newWalls0 = new File("../walls0");
						System.out.println(walls0.getAbsolutePath());
						System.out.println(newWalls0.getAbsolutePath());
						deleteFolder(walls0);
						System.out.println("folder deleted");
						try
						{
							copyFolder(newWalls0, walls0);
						}
						catch (IOException e)
						{
							System.out.println("could not copy directory");
							e.printStackTrace();
							return;
						}
						System.out.println("folders copied");
						
						bukkitWorld.setMonsterSpawnLimit(0);
						runCountdownTimer = new RunCountdownTimer(gameTimeInMin * 60);

						observers = new ArrayList<WallPlayer>();
						teams = new WallTeam[4];
						for (int i = 0; i < 4; i++)
						{
							teams[i] = new WallTeam();
						}
						
						gameStarted = false;
						
						bukkitWorld = Bukkit.getServer().createWorld(new WorldCreator("walls0"));						
						worldReadyForPlayers = true;
					}					
				},
				20 * 20);

	}

	// Deletes all files and subdirectories under dir.
	// Returns true if all deletions were successful.
	// If a deletion fails, the method stops attempting to delete and returns false.
	public boolean deleteFolder(File dir)
	{
	    if (dir.isDirectory())
	    {
	        String[] children = dir.list();
	        for (int i=0; i<children.length; i++)
	        {
	            boolean success = this.deleteFolder(new File(dir, children[i]));
	            if (!success)
	            {
	                return false;
	            }
	        }
	    }

	    // The directory is now empty so delete it
	    return dir.delete();
	}
	
	public static void copyFolder(File src, File dest) throws IOException
	{ 
		if(src.isDirectory())
		{
	 
			//if directory not exists, create it
			if(!dest.exists())
			{
				dest.mkdir();
				System.out.println("Directory copied from " 
						+ src + "  to " + dest);
			}
	 
			//list all the directory contents
			String files[] = src.list();
	 
			for (String file : files)
			{
				//construct the src and dest file structure
				File srcFile = new File(src, file);
				File destFile = new File(dest, file);
				//recursive copy
				copyFolder(srcFile,destFile);
			}
	 
		}
		else
		{
			//if file, then copy it
			//Use bytes stream to support all file types
			InputStream in = new FileInputStream(src);
			OutputStream out = new FileOutputStream(dest); 
			
			byte[] buffer = new byte[1024];
	 
			int length;
			//copy the file content in bytes 
			while ((length = in.read(buffer)) > 0)
			{
				out.write(buffer, 0, length);
			}
	 
			in.close();
			out.close();
			System.out.println("File copied from " + src + " to " + dest);
		}
	}
	
	public World getBukkitWorld()
	{
		return this.bukkitWorld;
	}

	public void playerDied(WallPlayer killie)
	{
		for (int i = 0; i < 4; i++)
		{
			teams[i].dispTeamChat(killie.getName() + " was defeated");
			teams[i].removePlayer(killie.getName());
		}
		
		// TODO: make sure this is what we want to do
		if (this.gameStarted && this.getNumPlayers() <= 1)
		{
			this.stop();
			this.gameStarted = false;
		}
	}

	public WallTeam getTeam(Integer teamId)
	{
		return this.teams[teamId];
	}

	public void removeObserver(WallPlayer wallPlayer)
	{
		this.observers.remove(wallPlayer);
	}

	public boolean hasStarted()
	{
		return this.gameStarted;
	}
}
